package com.hitqz.robot.driver.listener;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.google.common.util.concurrent.RateLimiter;
import com.hitqz.robot.api.business.event.TrackDynamicCheckEvent;
import com.hitqz.robot.api.business.event.TrackRemoteModeTriggerEvent;
import com.hitqz.robot.api.business.event.TrackStopTriggerEvent;
import com.hitqz.robot.api.business.event.TrackUltrasonicTriggerEvent;
import com.hitqz.robot.api.common.dto.robot.RobotGoTargetPosDto;
import com.hitqz.robot.api.common.dto.robot.RobotOnlineEventDto;
import com.hitqz.robot.api.common.entity.Robot;
import com.hitqz.robot.api.common.entity.RobotMapPos;
import com.hitqz.robot.driver.HitTrackRobotDriverImpl;
import com.hitqz.robot.driver.communication.NetworkServiceThreadPool;
import com.hitqz.robot.driver.dto.CalculateLocateDto;
import com.hitqz.robot.driver.dto.LocateStatusDto;
import com.hitqz.robot.driver.dto.PowerInfoDto;
import com.hitqz.robot.driver.dto.SelfCheckStatusDto;
import com.hitqz.robot.driver.model.PointDto;
import com.hitqz.robot.driver.util.TrackCacheUtil;
import com.hitqz.robot.driver.util.TrackToolkit;
import jakarta.annotation.PostConstruct;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

@Component
@AllArgsConstructor
@Slf4j
public class TrackSelfListener {

    // 创建一个具有单一线程的调度执行器
    private final static ScheduledExecutorService delayChargeScheduler = Executors.newScheduledThreadPool(1);
    // 运行中计数限流器
    private final static RateLimiter runningCountLimiter = RateLimiter.create(1);
    private final static RateLimiter chargeErrorRetryLimiter = RateLimiter.create(1);
    private final static AtomicInteger runningCount = new AtomicInteger(0);
    private static int lastMileage;
    private final static Map<String,String> dynamicRange = new HashMap<>(8);
    private final static List<Map<String,Integer>> mileage2pixel = new ArrayList<>(8);
    private static String currentArea = "";

    @PostConstruct
    private void init() {
        try {
            //fixme 后续修改为动态脚本
            String filePath = System.getProperty("user.dir") + File.separator + "mark.txt";
            File file = new File(filePath);
            if (!file.exists()) {
                log.info("[track]mark file not exist,please check");
            }else {
                List<String> lines = FileUtil.readLines(file, "utf-8");
                for (String line : lines) {
                    String[] split = line.split(",");
                    dynamicRange.put(split[0], split[1] + "," + split[2]);
                }
                log.info("[track]mark file load success:{}", dynamicRange);
            }
            filePath = System.getProperty("user.dir") + File.separator + "mileage2pixel.txt";
            file = new File(filePath);
            if (!file.exists()) {
                log.info("[track]mileage2pixel file not exist,please check");
            }else {
                List<String> lines = FileUtil.readLines(file, "utf-8");
                for (String line : lines) {
                    String[] split = line.split(",");
                    HashMap<String, Integer> map = new HashMap<>();
                    map.put("pixelX", Integer.parseInt(split[0]));
                    map.put("pixelY", Integer.parseInt(split[1]));
                    map.put("mileage", Integer.parseInt(split[2]));
                    mileage2pixel.add(map);
                }
                log.info("[track]mileage2pixel file load success:{}", mileage2pixel);
            }
        }catch (Exception e){
            log.info("获取动态检测范围失败");
        }
    }

    //定位事件处理
    @EventListener
    public void onLocationEvent(LocateStatusDto event){
        NetworkServiceThreadPool.dealEvent(()->{
            byte locatedStatus = event.getLocatedStatus();
            short lastId = event.getLastId();
            short nextId = event.getNextId();
            int mileage = event.getMileage();
            boolean isReach = false;
            String stationCode = String.valueOf(lastId);
            // 到达点位的标志 rfid和里程计模式
            if (locatedStatus==(byte)2) {
                //到达点位
                runningCount.set(0);
                isReach = true;
                //已经到了目标点，清空信息，避免误触发恢复前往的时间
                RobotMapPos dbPos = TrackToolkit.posMap.get(TrackToolkit.currentTargetCode);
                if (TrackToolkit.currentTargetCode.contains("-") &&
                    ArrayUtil.contains(TrackToolkit.currentTargetCode.split("-"), String.valueOf(lastId))) {
                    //这种是rfid模式到了
                    TrackToolkit.currentTargetCode = "";
                } else if (TrackToolkit.currentTargetCode.equals(String.valueOf(lastId))) {
                    // 这种情况是里程计模式到了
                    TrackToolkit.currentTargetCode = "";
                } else if (dbPos != null && dbPos.getLabel().matches("0+") && Math.abs(mileage-dbPos.getPosX())<20) {
                    // 这种情况是混合模式,里程到了 不需要TrackToolkit.currentTargetCode = "" 不然下一次就取不到这个到了状态了
                    stationCode = dbPos.getCode();
                    log.info("[track]混合模式到了{}",stationCode);
                }
                for (String s : CollUtil.emptyIfNull(TrackToolkit.posMap.keySet())) {
                    if (s.contains("-") && ArrayUtil.contains(s.split("-"), stationCode)){
                        stationCode = s;
                        break;
                    }
                }
                // 处理充电标志
                if (TrackToolkit.chargeFlag){
                    TrackToolkit.chargeFlag = false; // 重置充电标志
                    delayChargeScheduler.schedule(()->{
                        LocateStatusDto currentStatus = (LocateStatusDto) TrackCacheUtil.get(TrackCacheUtil.KEY_LOCATE_STATUS);
                        // 仍在坞站,可以充电
                        if (currentStatus.getLastId()==currentStatus.getNextId()
                                && currentStatus.getLastId() ==(short)0
                                && currentStatus.getLocatedStatus()==(byte)2
                                // 要排除掉：前往指定里程接口 nextId不会变的
                                && Math.abs(mileage)<=20) {
                            TrackToolkit.setChargeStatus((byte) 0);
                            try {
                                Thread.sleep(500);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                            TrackToolkit.setChargeStatus((byte) 1);
                        }else {
                            log.info("[track]delay charge but not in charge station now");
                        }
                    },3, TimeUnit.SECONDS);
                }
                //部分充电器充满后会断开，所以这边设置个低于85%重新充电
                PowerInfoDto powerInfoDto =(PowerInfoDto) Optional.ofNullable(TrackCacheUtil.get(TrackCacheUtil.KEY_POWER_INFO)).orElse(PowerInfoDto.builder().build());
                if (Math.abs(mileage)<=20
                    && powerInfoDto.getPercentage()<85
                    && powerInfoDto.getCurrentDirection()!=1
                    && chargeErrorRetryLimiter.tryAcquire(60*5)) {
                    TrackToolkit.setChargeStatus((byte) 0);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    TrackToolkit.setChargeStatus((byte) 1);
                    log.info("[track]充电故障且在坞站电量<85,重试充电");
                }

            }
            if (locatedStatus!=(byte)2 && lastMileage == mileage) {
                //行驶中且长时间保持不动,重新下发点位
                if (runningCountLimiter.tryAcquire()){
                    int count = runningCount.getAndIncrement();
                    if (count > 60){
                        //60秒内不动
                        runningCount.set(0);
                        if (StrUtil.isNotBlank(TrackToolkit.currentTargetCode)) {
                            log.info("[track]long time no go,re sent go target,{}",TrackToolkit.currentTargetCode);
                            HitTrackRobotDriverImpl service = SpringUtil.getBean(HitTrackRobotDriverImpl.class);
                            service.robotTaskGoTarget(RobotGoTargetPosDto.builder().code(TrackToolkit.currentTargetCode).build());
                        }
                    }
                }
            }
            lastMileage = mileage;
            //计算像素坐标
            int pixelX = 0;
            int pixelY = 0;
            if (mileage2pixel.isEmpty()) {
                if (lastId == -1 && nextId == -1) {
                    //刚开机位置不确定
                } else if (lastId == -1) {
                    //扫到错误标签
                } else {
                    RobotMapPos lastPos = TrackToolkit.posMap.get(stationCode);
                    //todo 站点中间的进行计算
                    pixelX = lastPos.getPixelX();
                    pixelY = lastPos.getPixelY();
                }
            }else {
                //换算
                // 低于起点，返回起点坐标
                try {
                    if (mileage < mileage2pixel.get(0).get("mileage")) {
                        pixelX = mileage2pixel.get(0).get("pixelX");
                        pixelY = mileage2pixel.get(0).get("pixelY");
                    }

                    // 超过终点，返回终点坐标
                    if (mileage > mileage2pixel.get(mileage2pixel.size() - 1).get("mileage")) {
                        pixelX = mileage2pixel.get(mileage2pixel.size() - 1).get("pixelX");
                        pixelY = mileage2pixel.get(mileage2pixel.size() - 1).get("pixelY");
                    }

                    // 在范围内，进行插值计算
                    for (int i = 0; i < mileage2pixel.size() - 1; i++) {
                        Integer pixelX1 = mileage2pixel.get(i).get("pixelX");
                        Integer pixelY1 = mileage2pixel.get(i).get("pixelY");
                        Integer mileage1 = mileage2pixel.get(i).get("mileage");

                        Integer pixelX2 = mileage2pixel.get(i+1).get("pixelX");
                        Integer pixelY2 = mileage2pixel.get(i+1).get("pixelY");
                        Integer mileage2 = mileage2pixel.get(i+1).get("mileage");

                        if (mileage >= mileage1 && mileage <= mileage2) {
                            double ratio = (double) (mileage - mileage1) / (mileage2 - mileage1);
                            pixelX = (int)(pixelX1 + ratio * (pixelX2 - pixelX1));
                            pixelY =(int) (pixelY1 + ratio * (pixelY2 - pixelY1));
                            break;
                        }
                    }
                }catch (Exception e){
                    log.error("坐标转化异常,e:{}",e.getMessage());
                }
            }
            CalculateLocateDto calculateLocateDto = CalculateLocateDto.builder()
                    .currentStation(stationCode)
                    .isReach(isReach)
                    .pixelX(pixelX)
                    .pixelY(pixelY)
                    .build();
            TrackCacheUtil.put(TrackCacheUtil.KEY_CALCULATE_LOCATION, calculateLocateDto);
        });
    }




    @EventListener
    public void onUltrasonicEvent(TrackUltrasonicTriggerEvent event){
        NetworkServiceThreadPool.dealEvent(()->{
            try {
                if (event.getFrontUltrasonicTriggerStatus() == 1 || event.getBackUltrasonicTriggerStatus() == 1) {
                    //超声波触发
                    return;
                }
                // 超声波释放
                if (StrUtil.isBlank(TrackToolkit.currentTargetCode)) {
                    log.info("ultrasonic release,no target");
                    return;
                }
                Thread.sleep(1000);
                HitTrackRobotDriverImpl service = SpringUtil.getBean(HitTrackRobotDriverImpl.class);
                service.robotTaskGoTarget(RobotGoTargetPosDto.builder().code(TrackToolkit.currentTargetCode).build());
                log.info("[track]ultrasonic release, go to target,{}", TrackToolkit.currentTargetCode);
            }catch (Exception e){
                log.error("[track]ultrasonic release error",e);
            }
        });
    }

    @EventListener
    public void onRemoteTriggerEvent(TrackRemoteModeTriggerEvent event){
        NetworkServiceThreadPool.dealEvent(()->{
            try {
                if (event.getRemoteTriggerStatus() == 1) {
                    return;
                }
                // 远程模式释放
                if (StrUtil.isBlank(TrackToolkit.currentTargetCode)) {
                    log.info("remote release,no target");
                    return;
                }
                Thread.sleep(1000);
                HitTrackRobotDriverImpl service = SpringUtil.getBean(HitTrackRobotDriverImpl.class);
                service.robotTaskGoTarget(RobotGoTargetPosDto.builder().code(TrackToolkit.currentTargetCode).build());
                log.info("[track]remote release, go to target,{}", TrackToolkit.currentTargetCode);
            }catch (Exception e){
                log.info("[track]remote release error",e);
            }
        });
    }


    @EventListener
    public void onStopTriggerEvent(TrackStopTriggerEvent event){
        NetworkServiceThreadPool.dealEvent(()->{
            try {
                if (event.getStopTriggerStatus() == 1) {
                    return;
                }
                // 急停释放
                if (StrUtil.isBlank(TrackToolkit.currentTargetCode)) {
                    log.info("stop release,no target");
                    return;
                }
                Thread.sleep(1000);
                HitTrackRobotDriverImpl service = SpringUtil.getBean(HitTrackRobotDriverImpl.class);
                service.robotTaskGoTarget(RobotGoTargetPosDto.builder().code(TrackToolkit.currentTargetCode).build());
                log.info("[track]stop release, go to target,{}", TrackToolkit.currentTargetCode);
            }catch (Exception e){
                log.info("[track]stop release error",e);
            }
        });
    }

    @EventListener
    public void onOnlineEvent(RobotOnlineEventDto event){
        //上线逻辑
        if (Objects.equals(event.getOnlineStatus(),1)){
            if (StrUtil.isNotBlank(TrackToolkit.currentTargetCode)){
                HitTrackRobotDriverImpl service = SpringUtil.getBean(HitTrackRobotDriverImpl.class);
                service.robotTaskGoTarget(RobotGoTargetPosDto.builder().code(TrackToolkit.currentTargetCode).build());
                log.info("[track]online, go to target,{}", TrackToolkit.currentTargetCode);
            }
        }


    }


    @Scheduled(fixedDelay = 1000L)
    private void setDynamicRange(){
        Set<String> keys = dynamicRange.keySet();
        if (keys.isEmpty()){
            return;
        }

        for (String key : keys) {
            String range = dynamicRange.get(key);
            String[] split = range.split(",");
            int min = Integer.parseInt(split[0]);
            int max = Integer.parseInt(split[1]);
            LocateStatusDto locationStatus = TrackToolkit.getLocationStatus();
            int mileage = locationStatus.getMileage();
            if (min < mileage && mileage < max && !Objects.equals(key,currentArea)){
                TrackDynamicCheckEvent event = TrackDynamicCheckEvent.builder().poseGroupName(key).build();
                SpringUtil.getApplicationContext().publishEvent(event);
                log.info("[track]发布调整云台姿态命令");
                currentArea = key;
                log.info("[track]进入：{}",key);
            }

        }
    }



}
